<template>
<div class="container-fluid" style="padding: 5px 10px 35px 10px;">
    <br><br class="hidden-xs">
    <div class="row hidden-xs">
        <div class="text-center">
            <el-button-group class="player-btn">
                <el-button type="primary" size="medium" v-for="list in playerBtnDate" :key="list.num" @click.prevent="setPlayerLength(list.num)" :class="{'active' : playerLength == list.num}">{{list.name}}</el-button>
                <el-button type="primary" size="medium" v-if="!isIE()" @click.prevent="loopPlaying ? stopLoopPlay() : showLoopPlayListDlg()" :title="loopPlaying ? '停止轮巡播放':'选择轮巡播放通道'">
                    <i style="color:#dd4b39;" class="fa fa-stop" v-if="loopPlaying"></i>
                    <span v-else>轮播</span>
                </el-button>
                <el-button type="primary" size="medium" @click.prevent="fullscreen" title="全屏显示"><i class="fa fa-arrows-alt"></i></el-button>
                <el-button type="primary" size="medium" v-if="!loopPlaying && hasLocalData()" @click.prevent="loadLocalData(true)" title="点击加载上次记录"><i class="fa fa-database"></i></el-button>
            </el-button-group>
        </div>
    </div>
    <div class="view-list row">
        <div class="visible-xs col-xs-12 text-center">
            <el-button-group class="player-btn">
                <el-button type="primary" size="mini" v-for="list in playerBtnDate" :key="list.num" @click.prevent="setPlayerLength(list.num)" :class="{'active' : playerLength == list.num}">{{list.name}}</el-button>
            </el-button-group>
        </div>
        <br><br><br class="visible-xs">
        <div class="clearfix"></div>
        <div id="group-tree-left" class="col-md-3 col-lg-2">
            <div autocomplete="off" spellcheck="false">
                <div class="form-group form-group-sm">
                    <!-- <label>搜索</label> -->
                    <input type="text" class="form-control" placeholder="搜索" v-model.trim="q" @keydown.enter.prevent ref="q">
                </div>
            </div>
            <!-- <ul class="nav nav-tabs">
              <li class="active"><a href="#group-tree-wrapper" data-toggle="tab" style="background-color:transparent;">分组树</a></li>
            </ul> -->
            <div class="tab-content" style="margin: 10px 0;" id="tab-tree-wrapper">
              <div class="tab-pane active" ref="groupTreeWrapper" id="group-tree-wrapper">
                <el-tree ref="groupTree" id="group-tree" node-key="key" v-if="showTree"
                  :props="treeProps" :filter-node-method="treeFilter" :load="groupTreeLoad" lazy :empty-text="groupTreeEmptyText" :default-expanded-keys="defExpandGroups"
                  :draggable="hasAnyRole(serverInfo, userInfo, '管理员', '超级管理员') && !isMobile()" :allow-drag="allowDrag" :allow-drop="allowDrop" @node-drop="handleDrop"
                  @node-click="treeNodeClick" @node-contextmenu="treeNodeRightClick" >
                  <span class="custom-tree-node" slot-scope="{node, data}">
                    <span :class="{'text-green': data.Channel && data.Online && (!data.SourceVideoCodecName || data.SourceVideoCodecName == 'h264'),
                      'text-orange': data.Channel && data.Online && data.SourceVideoCodecName && data.SourceVideoCodecName != 'h264',
                      'text-bold': !!data.GBID||!!data.GBID2}">
                      <i :class="['fa', {'fa-home': data.key == 'ROOT',
                        'fa-sitemap' : data.GroupID && data.GroupID != 'ROOT',
                        'fa-camera': data.Channel,
                        'fa-spinner': node.playing,
                        'fa-spin': node.playing,
                        'fa-play': node.play,
                      }]"></i>
                      <span v-if="node.level > 1 || q || isMobile() || !hasAnyRole(serverInfo, userInfo, '管理员')" class="ellipsis" style="user-select:none;" :title="formatNodeTitle(node, data)">{{node.label}}</span>
                      <el-tooltip v-else content="右键创建分组" placement="right" manual :value="showTip">
                        <span class="ellipsis" style="user-select:none;" :title="node.label">{{node.label}}</span>
                      </el-tooltip>
                    </span>
                  </span>
                </el-tree>
                <VueContextMenu class="right-menu" :target="contextMenuTarget" :show="contextMenuVisible" @update:show="(show) => contextMenuVisible = show">
                    <a role="button" @click="playStop(contextMenuNode)" v-show="contextMenuNodeData && contextMenuNodeData.Channel && contextMenuNode && contextMenuNode.play && !contextMenuNode.playing">
                        <i class="fa fa-stop"></i> 停止播放
                    </a>
                    <a role="button" @click="treeNodeRefresh(pnode)" v-show="contextMenuNodeData">
                        <i class="fa fa-refresh"></i> 刷新节点
                    </a>
                    <a role="button" @click="treeAddNode" v-show="contextMenuNodeData && contextMenuNodeData.GroupID && userInfo && (hasAllChannel(serverInfo, userInfo) || (userInfo.id == contextMenuNodeData.CreatorID) || (contextMenuNodeData.GroupID != 'ROOT' && contextMenuNodeData.SubCount > 0))">
                        <i class="fa fa-plus"></i> 新建分组
                    </a>
                    <a role="button" @click="treeShowChannelListDlg" v-show="contextMenuNodeData && contextMenuNodeData.GroupID && contextMenuNodeData.GroupID != 'ROOT' && userInfo && hasAllChannel(serverInfo, userInfo)">
                        <i class="fa fa-check"></i> 选择通道
                    </a>
                    <a role="button" @click="treeEditNode" v-show="contextMenuNodeData && contextMenuNodeData.GroupID && contextMenuNodeData.GroupID != 'ROOT' && userInfo && (hasAllChannel(serverInfo, userInfo) || (userInfo.id == contextMenuNodeData.CreatorID))">
                        <i class="fa fa-edit"></i> 编辑分组
                    </a>
                    <a role="button" @click="treeEditNode" v-show="contextMenuNodeData && contextMenuNodeData.GroupID && contextMenuNodeData.GroupID == 'ROOT' && userInfo && hasAnyRole(serverInfo, userInfo, '超级管理员')">
                        <i class="fa fa-edit"></i> 编辑名称
                    </a>
                    <a role="button" @click="treeRemoveNode" v-show="contextMenuNodeData && contextMenuNodeData.GroupID && contextMenuNodeData.GroupID != 'ROOT' && userInfo && (hasAllChannel(serverInfo, userInfo) || (userInfo.id == contextMenuNodeData.CreatorID))">
                        <i class="fa fa-remove"></i> 删除分组
                    </a>
                    <a role="button" @click="treeRemoveNode" v-show="contextMenuNodeData && !contextMenuNodeData.GroupID && contextMenuNodeData.Channel && pnode && pnode.data && pnode.data.GroupID && pnode.data.GroupID != 'ROOT' && userInfo && hasAllChannel(serverInfo, userInfo)">
                        <i class="fa fa-remove"></i> 移出分组
                    </a>
                </VueContextMenu>
              </div>
            </div>
        </div>
        <div id="group-tree-right" class="row col-md-9 col-lg-10">
            <div id="screen-sticky">
                <div class="video-show col-md-9 col-lg-10" id="group-tree-player">
                    <div>
                        <div class="no-margin no-padding video" v-for="(player,index) in players" :key="index" @mousemove="resetCloseTimer(player)" @touchstart="resetCloseTimer(player)"
                            @click="clickPlayer(player, index, $event)" @touchend="clickPlayer(player, index, $event)"
                            :class="{'active': index == playerIdx, 'col-sm-12': playerLength == 1,'col-sm-6': playerLength == 4,'col-sm-4': playerLength == 9,'col-sm-3': playerLength == 16}">
                            <LivePlayer :videoUrl="player.url" :waterMark="player.osd"
                                :hideBigPlayButton="!!serverInfo.HideBigPlayButton || loopPlaying" :aspect="aspect"
                                :poster="player.poster" :controls="player.bControls && !loopPlaying" live muted stretch
                                @fullscreen="onFullscreenChange(player, index, $event)" @media_info="onMediaInfo(player, index, $event)" @ended="onEnded(player, index, $event)"
                                @error="onError(player, index, $event)" @message="$message"
                                v-loading="player.bLoading" :loading.sync="player.bLoading" element-loading-text="加载中..." element-loading-background="#000">
                                <div class="ptz-block ptz-block-fs" v-show="!isMobile() && hasAnyRole(serverInfo, userInfo, '管理员', '操作员') && canPTZ && player.bFullscreen && player.bControls">
                                    <div class="ptz-cell ptz-up" @mousedown.prevent="ptzControl" command="up" title="上">
                                        <i class="fa fa-chevron-up"></i>
                                    </div>
                                    <div class="ptz-cell ptz-left" @mousedown.prevent="ptzControl" command="left" title="左">
                                        <i class="fa fa-chevron-left"></i>
                                    </div>
                                    <div class="ptz-cell ptz-center" title="云台控制">
                                        <i class="fa fa-arrows"></i>
                                    </div>
                                    <div class="ptz-cell ptz-right" @mousedown.prevent="ptzControl" command="right" title="右">
                                        <i class="fa fa-chevron-right"></i>
                                    </div>
                                    <div class="ptz-cell ptz-down" @mousedown.prevent="ptzControl" command="down" title="下">
                                        <i class="fa fa-chevron-down"></i>
                                    </div>
                                    <div class="ptz-cell ptz-zoomin" @mousedown.prevent="ptzControl" command="zoomin" title="放大">
                                        <i class="fa fa-plus-circle"></i>
                                    </div>
                                    <div class="ptz-cell ptz-talk" v-if="canTalk() && talkEnable">
                                        <i class="fa fa-microphone" @mousedown.prevent="talkStart"></i>
                                    </div>
                                    <div class="ptz-cell ptz-zoomout" @mousedown.prevent="ptzControl" command="zoomout" title="缩小">
                                        <i class="fa fa-minus-circle"></i>
                                    </div>
                                </div>
                            </LivePlayer>
                            <div class="video-title" v-if="bVideoTitle && player.title" :title="player.title">{{player.title}}</div>
                            <div class="video-close" v-show="player.url && player.bCloseShow && !loopPlaying" v-on:click="closeVideo(index,true)">关闭</div>
                            <div class="video-close channel-selector" v-show="!player.url && player.bCloseShow && !loopPlaying" v-on:click="selectChannel(index,player)">选择通道</div>
                        </div>
                        <ScreenChannelListDlg ref="channelListFullscreenDlg" @selected="play" :title="channelListDlgTitle"></ScreenChannelListDlg>
                    </div>
                    <div class="clearfix"></div>
                    <div class="text-center" v-if="isDemoUser(serverInfo, userInfo) && outHevcTipIdx < 0">
                        <br>
                        提示: 演示系统限制匿名登录播放时间, 若需测试长时间播放, 请<a target="_blank" href="//www.liveqing.com/docs/download/LiveNVR.html">下载使用</a>
                    </div>
                    <div class="text-center text-red" v-if="outHevcTipIdx >= 0">
                        <br>
                        提示: 正在播放 H265 直出流, 确保浏览器版本较新, 并且开启硬件加速
                    </div>
                </div>
                <div class="col-md-3 col-lg-2" id="group-tree-ptz" v-if="hasAnyRole(serverInfo, userInfo, '管理员', '操作员')">
                    <div class="ptz-block">
                        <el-progress class="mic-level-bar" :percentage="level" :text-inside="true" v-if="ws"></el-progress>
                        <div class="ptz-cell ptz-up" :class="{'readonly': !playing || loopPlaying || !canPTZ }" command="up" title="上" @mousedown.prevent="ptzControl" @touchstart.prevent="ptzControl">
                            <i class="fa fa-chevron-up"></i>
                        </div>
                        <div class="ptz-cell ptz-left" :class="{'readonly': !playing || loopPlaying || !canPTZ }" command="left" title="左" @mousedown.prevent="ptzControl" @touchstart.prevent="ptzControl">
                            <i class="fa fa-chevron-left"></i>
                        </div>
                        <div class="ptz-cell ptz-center" title="云台控制">
                            <i class="fa fa-microphone" title="按住喊话" @mousedown.prevent="talkStart"  @touchstart.prevent="talkStart" v-if="canTalk() && talkEnable && playing && !loopPlaying"></i>
                            <i class="fa fa-microphone-slash" title="由于浏览器安全策略, 非 HTTPS 或 localhost 访问, 对讲不可用" v-if="!canTalk() && talkEnable && playing && !loopPlaying"></i>
                        </div>
                        <div class="ptz-cell ptz-right" :class="{'readonly': !playing || loopPlaying || !canPTZ }" command="right" title="右" @mousedown.prevent="ptzControl" @touchstart.prevent="ptzControl">
                            <i class="fa fa-chevron-right"></i>
                        </div>
                        <div class="ptz-cell ptz-down" :class="{'readonly': !playing || loopPlaying || !canPTZ }" command="down" title="下" @mousedown.prevent="ptzControl" @touchstart.prevent="ptzControl">
                            <i class="fa fa-chevron-down"></i>
                        </div>
                        <div class="ptz-cell ptz-zoomin" :class="{'readonly': !playing || loopPlaying || !canPTZ }" command="zoomin" title="放大" @mousedown.prevent="ptzControl" @touchstart.prevent="ptzControl">
                            <i class="fa fa-plus"></i>
                        </div>
                        <div class="ptz-cell ptz-zoomout" :class="{'readonly': !playing || loopPlaying || !canPTZ }" command="zoomout" title="缩小" @mousedown.prevent="ptzControl" @touchstart.prevent="ptzControl">
                            <i class="fa fa-minus"></i>
                        </div>
                    </div>
                    <div class="settings-block">
                        <el-checkbox v-model.trim="bVideoTitle" name="bVideoTitle">
                            显示名称
                        </el-checkbox>
                    </div>
                </div> <!-- group-tree-ptz -->
                <div class="clearfix"></div>
                <br><br class="hidden-xs">
                <div id="screen-sticky-bottom" class="row hidden-xs">
                    <div class="text-center col-md-11 col-lg-10">
                        <el-button-group class="player-btn">
                            <el-button type="primary" size="medium" v-for="list in playerBtnDate" :key="list.num" @click.prevent="setPlayerLength(list.num)" :class="{'active' : playerLength == list.num}">{{list.name}}</el-button>
                            <el-button type="primary" size="medium" v-if="!isIE()" @click.prevent="loopPlaying ? stopLoopPlay() : showLoopPlayListDlg()" :title="loopPlaying ? '停止轮巡播放':'选择轮巡播放通道'">
                                <i style="color:#dd4b39;" class="fa fa-stop" v-if="loopPlaying"></i>
                                <span v-else>轮播</span>
                            </el-button>
                            <el-button type="primary" size="medium" @click.prevent="fullscreen" title="全屏显示"><i class="fa fa-arrows-alt"></i></el-button>
                            <el-button type="primary" size="medium" v-if="!loopPlaying && hasLocalData()" @click.prevent="loadLocalData(true)" title="点击加载上次记录"><i class="fa fa-database"></i></el-button>
                        </el-button-group>
                    </div>
                    <div class="clearfix"></div>
                    <br><br class="hidden-xs">
                </div>
            </div> <!-- screen-sticky -->
        </div> <!-- group-tree-right -->
    </div> <!-- view-list -->
    <ScreenChannelListDlg ref="channelListDlg" @selected="play" :token="token" :title="channelListDlgTitle"></ScreenChannelListDlg>
    <ScreenLoopPlayListDlg ref="loopPlayListDlg"  @loopPlay="startLoopPlay" :token="token"></ScreenLoopPlayListDlg>
    <GroupEditDlg ref="nodeEditDlg" @submit="onEditGroup"></GroupEditDlg>
    <GroupChannelListDlg ref="nodeChannelListDlg" @hide="treeNodeRefresh(pnode)" :title="nodeChannelListDlgTitle"></GroupChannelListDlg>
</div>
</template>

<script>
import "assets/js/jquery-sticky.js";
import "jquery-ui/ui/widgets/draggable";
import GroupEditDlg from "components/GroupEditDlg.vue";
import GroupChannelListDlg from "components/GroupChannelListDlg.vue";
import ScreenChannelListDlg from "components/ScreenChannelListDlg.vue";
import ScreenLoopPlayListDlg from "components/ScreenLoopPlayListDlg.vue";
import { component as VueContextMenu } from "@penggy/vue-context-menu";
import LivePlayer from "@liveqing/liveplayer";
import _ from "lodash";
import { mapState } from "vuex";

export default {
    components: {
        LivePlayer,
        ScreenChannelListDlg,
        GroupEditDlg,
        GroupChannelListDlg,
        VueContextMenu,
        ScreenLoopPlayListDlg,
    },
    data() {
        return {
            q: "",
            protocol: "",
            defExpandGroups: ['ROOT'],
            showTree: true,
            showTip: false,
            groupTreeLoading: false,
            fullscreenFlag: false,
            playerIdx: 0,
            players: [],
            playerLength: 4,
            channelListDlgTitle: "",
            playerBtnDate: [{
                num: 1,
                name: "单屏"
            }, {
                num: 4,
                name: "四分屏"
            }, {
                num: 9,
                name: "九分屏"
            }, {
                num: 16,
                name: "十六分屏"
            }],
            loadedData: false,
            localData: {
                num1: {},
                num4: {},
                num9: {},
                num16: {}
            },
            aspect:"16:9",
            pnode: null,
            contextMenuTarget: null,
            contextMenuVisible: false,
            contextMenuNodeData: null,
            contextMenuNode: null,
            treeProps: {
                label: (data, node) => {
                    node.id = data.GroupID || data.Channel;
                    node.name = data.Name || "未命名";
                    var label = data.Name || "未命名";
                    if(data.GroupID && data.SubCount > 0) {
                        label += ` [${data.OnlineSubCount}/${data.SubCount}]`;
                    }
                    return label;
                },
                isLeaf: (data, node) => {
                    return !!data.Channel;
                },
                // children: "Children",
                disabled: (data, node) => {
                    return data.Channel && data.Online == 0;
                }
            },
            nodeChannelListDlgTitle: "选择通道",
            token: "",
            loopChannelIndex: 0,
            loopPlayerIndex: 0,
            loopPlaying: false,
            loopChannels: [],
            loopInterval: 10,
            loopTimer: 0,
            bPlayerFullscreen: false, // any player is fullscreen
            outHevcTipIdx: -1, // idx of player is out hevc stuck
            bVideoTitle: false, // wether display video title
            recorder: null,
            ws: null,
            level: 0,
        };
    },
    created() {
        var aspect = this.getQueryString("aspect","").replace("x", ":");
        if (aspect != "") {
            this.aspect = aspect
        } else if (window.screen.width > window.screen.height) {
            this.aspect = window.screen.width+":"+window.screen.height;
        }
        this.token = this.getQueryString("token","");
    },
    mounted() {
        if (this.isIE() || this.isMobile()) {
            this.playerBtnDate = [{
                num: 1,
                name: "单屏"
            }, {
                num: 4,
                name: "四分屏"
            }, {
                num: 9,
                name: "九分屏"
            }];
        }
        if (this.isMobile()) {
            this.playerLength = 1;
        }
        this.setPlayerLength(this.playerLength);
        this.protocol = this.getQueryString("protocol", this.serverInfo.PreferStreamFmt||"");
        this.loadLocalData(false);
        this.contextMenuTarget = document.querySelector('#tab-tree-wrapper');
        $(document).on("mouseup touchend", this.mouseUp);
        $(document).on('expanded.pushMenu collapsed.pushMenu', this.updateSticky);
        $("#screen-sticky").sticky({ bottomSpacing: 40 });
    },
    watch: {
        q: function(newVal, oldVal) {
            if(!newVal) {
                this.treeRefresh();
                return;
            }
            this.doDelaySearch();
        },
    },
    methods: {
        getQueryString(name, defVal = "") {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) {
                return unescape(r[2]);
            }
            return defVal;
        },
        doDelaySearch: _.debounce(function() {
            this.$refs['groupTree'].filter(this.q);
        }, 500),
        hasLocalData() {
            if (localStorage['livenvr_screen']) {
                for (let index = 0; index < this.playerLength; index++) {
                    if (this.localData["num" + this.playerLength]["c" + index]) {
                        return !this.loadedData;
                    }
                }
                this.loadedData = true;
            }
            return false
        },
        setLocalData() {
            this.loadedData = true;
            localStorage['livenvr_screen'] = JSON.stringify(this.localData);
        },
        loadLocalData(update) {
            if (localStorage['livenvr_screen']) {
                this.localData = JSON.parse(localStorage['livenvr_screen']);
            }
            if (update) {
                if(this.isDemoUser(this.serverInfo, this.userInfo) && this.playerLength > 4) {
                    this.$message({
                        type: "error",
                        message: "演示系统请在四分屏体验, 或下载使用"
                    })
                    return
                }
                this.loadedData = true;
                for (let index = 0; index < this.playerLength; index++) {
                    if (this.localData["num" + this.playerLength]["c" + index]) {
                        var c = JSON.parse(this.localData["num" + this.playerLength]["c" + index])
                        this.play(index, c, false);
                    }
                }
            }
        },
        selectChannel(index) {
            this.showTip = false;
            this.channelListDlgTitle = `为第 ${index+1} 屏选择通道`;
            if(this.fullscreenFlag) {
                this.$refs["channelListFullscreenDlg"].show(index);
            } else {
                this.$refs["channelListDlg"].show(index);
            }
        },
        setPlayerLength(playerNum) {
            if (playerNum == this.players.length) return;
            this.ctrlStop();
            this.clearVideos();
            this.loadedData = false;
            this.players = []
            this.playerIdx = 0;
            this.playerLength = playerNum;
            for (let index = 0; index < this.playerLength; index++) {
                this.players.push({
                    url: "",
                    bLoading: false,
                    bCloseShow: false,
                    bControls: false,
                    bFullscreen: false,
                    talkEnable: false,
                    closeTimer: 0,
                    closeInterval: 0,
                    deviceType: "",
                    poster: "",
                    protocol: "",
                    title: "",
                    osd: "",
                    mediaInfo: null,
                    sourceVideoCodecName: "",
                    sourceVideoWidth: 0,
                    sourceVideoHeight: 0,
                })
            }
        },
        play(index, channel, next) {
            var i = 0;
            // if (channel.Online == 0 && this.serverInfo.ChannnelSnapInterval) {
            //     this.$message({
            //         type: "error",
            //         message: "当前设备不在线！"
            //     });
            //     return;
            // }
            var player = null;
            for (var _player of this.players) {
                if (index == i) {
                    player = _player;
                    break;
                }
                i++
            }
            if (!player) {
                this.$message({
                    type: "error",
                    message: "当前播放窗口已被占满！"
                });
                return
            }
            if (player.bLoading) {
                return
            }
            player.bLoading = true;
            if (next) {
                this.setPlayerIdx(index + 1);
            }
            if(channel.Node) {
                player.node = channel.Node;
                this.$set(player.node, "playing", true);
                delete channel.Node;
            }
            var protocol = "HLS";
            if (this.flvSupported()) {
                if(this.players.length < 6 && !this.isIE()) {
                    protocol = "FLV";
                } else {
                    protocol = "WS_FLV";
                }
            } else if (this.rtcSupported()) {
                protocol = "WEBRTC";
            }
            switch(String(this.protocol).toUpperCase()) {
                case "WEBRTC":
                    if(this.rtcSupported()) {
                        protocol = "WEBRTC";
                    }
                    break;
                case "FLV":
                    if(this.players.length < 6 && this.flvSupported() && !this.isIE()) {
                        protocol = "FLV";
                        break;
                    }
                case "WS_FLV":
                case "WS-FLV":
                    if(this.flvSupported()) {
                        protocol = "WS_FLV";
                    } else {
                        protocol = "HLS";
                    }
                    break;
                case "HLS":
                    protocol = "HLS";
                    break;
            }
            var param = {
                Channel: channel.Channel,
                Protocol: protocol,
            }
            if (this.token) {
                param.token = this.token;
            }
            $.get("/api/v1/getchannelstream", param).then(data => {
                var videoUrl = String((data.LiveQing || data.NVR).Body.URL).replace("{host}", location.hostname);
                var errNum = (data.LiveQing || data.NVR).Header.ErrorNum;
                var errString = (data.LiveQing || data.NVR).Header.ErrorString;
                if (!videoUrl || errNum != "200") {
                    player.bLoading = false;
                    if(player.node) {
                        this.$delete(player.node, "playing");
                    }
                    if(errNum != "200" && errString) {
                        this.$message({
                            type: "error",
                            message: errString,
                        })
                    }
                    return
                }
                if (protocol == "WS_FLV" && videoUrl.indexOf("ws") != 0) {
                    var prefix = "ws:";
                    if (location.protocol == "https:") {
                        prefix = "wss:";
                    }
                    videoUrl = prefix + "//" + location.host + videoUrl;
                }
                player.poster = protocol == "RTMP" ? "" : ((data.LiveQing || data.NVR).Body.SnapURL || "");
                player.title = (data.LiveQing || data.NVR).Body.ChannelName || "";
                player.osd = (data.LiveQing || data.NVR).Body.OSD;
                player.deviceType = (data.LiveQing || data.NVR).Body.DeviceType;
                player.talkEnable = (data.LiveQing || data.NVR).Body.TalkEnable || false;
                player.channel = channel.Channel;
                player.sourceVideoCodecName = channel.SourceVideoCodecName;
                player.sourceVideoWidth = channel.SourceVideoWidth;
                player.sourceVideoHeight = channel.SourceVideoHeight;
                player.protocol = protocol;
                player.url = videoUrl; // no need in next tick since player@2.6.9
                if(player.node) {
                    this.$delete(player.node, "playing");
                    var play = player.node.play || [];
                    if(!!videoUrl) {
                        play.push(index);
                    }
                    if(play.length) {
                        this.$set(player.node, "play", play);
                    }
                }
                this.resetCloseTimer(player);
                if (!this.loadedData) {
                    this.localData["num" + this.playerLength] = {}
                }
                this.localData["num" + this.playerLength]["c" + index] = JSON.stringify(channel)
                this.setLocalData();
            }).fail(() => {
                this.$nextTick(() => {
                    player.bLoading = false;
                    if(player.node) {
                        this.$delete(player.node, "playing");
                    }
                })
            })
        },
        closeVideo(idx, manual = false) {
            var player = this.players[idx]
            if (!player) return;
            if (player.closeTimer) {
                clearTimeout(player.closeTimer);
                player.closeTimer = 0;
            }
            if (this.outHevcTipIdx == idx) {
                this.outHevcTipIdx = -1;
            }
            if (player.node) {
                var play = player.node.play || [];
                play = play.filter(val => val != idx);
                if (play.length) {
                    this.$set(player.node, "play", play);
                } else {
                    this.$delete(player.node, "play");
                    this.$delete(player.node, "playing");
                }
                delete player.node;
            }
            player.mediaInfo = null;
            player.bCloseShow = false;
            player.bControls = false;
            player.bLoading = false;
            player.bFullscreen = false;
            player.talkEnable = false;
            player.osd = "";
            player.poster = "";
            player.protocol = "";
            player.title = "";
            player.url = "";
            player.deviceType = "";
            player.channel = 0;
            player.sourceVideoCodecName = "";
            player.sourceVideoWidth = 0;
            player.sourceVideoHeight = 0;
            if (manual) {
                delete this.localData["num" + this.playerLength]["c" + idx];
                this.setLocalData();
            }
        },
        onMediaInfo(player, idx, mi) {
            player.mediaInfo = mi;
        },
        onEnded(player, idx, e) {
            player.mediaInfo = null;
        },
        onError(player, idx, e) {
            if(e == 'MediaError' && ((player.mediaInfo && String(player.mediaInfo.videoCodec).startsWith("hvc")) || (player.protocol == "HLS" && player.sourceVideoCodecName == "hevc"))) {
                this.outHevcTipIdx = idx;
                console.log("提示: 正在播放 H265 直出流, 确保浏览器版本较新, 并且开启硬件加速");
            }
        },
        onFullscreenChange(player, idx, bFullscreen) {
            if(player) {
                player.bFullscreen = bFullscreen;
                this.bPlayerFullscreen = bFullscreen;
                if(bFullscreen) {
                    $(`#group-tree-right .video-show .video:eq(${idx}) .ptz-block-fs`).draggable({
                        handle: '.ptz-center',
                        // cancel: '.ptz-cell',
                        containment: `#group-tree-right .video-show .video:eq(${idx}) .video-js`,
                        delay: 100,
                    });
                } else {
                    $(`#group-tree-right .video-show .video:eq(${idx}) .ptz-block-fs`).draggable('destroy');
                }
            }
        },
        fullscreen() {
            // if (this.isMobile()) {
            //     this.$message({
            //         type: "error",
            //         message: "请在电脑浏览器上使用该功能"
            //     });
            //     return;
            // }
            this.$fullscreen.enter(this.$el.querySelector(`.video-show > div`), {
                wrap: false,
                callback: f => {
                    this.fullscreenFlag = f;
                }
            });
        },
        resetCloseTimer(player) {
            player.bCloseShow = true;
            player.bControls = true;
            if (player.closeTimer) {
                clearTimeout(player.closeTimer)
            }
            player.closeTimer = setTimeout(() => {
                player.bCloseShow = false;
                player.closeInterval = 0;
                if ($(this.$el).find(".ptz-cell.active").length == 0 && $(this.$el).find(".fa-microphone.active").length == 0) {
                    player.bControls = false;
                }
            }, player.closeInterval || 2000);
        },
        groupTreeLoad(node, resolve) {
            this.groupTreeLoading = true;
            var param = {
                pid: node.key || "",
            }
            if (this.token) {
                param.token = this.token;
            }
            $.get("/api/v1/group/tree", param).then(ret => {
                var totalSize = 0;
                var groupSize = 0;
                resolve((ret||[]).map(v => {
                    totalSize++;
                    if(v.GroupID) {
                        groupSize++;
                    }
                    return Object.assign(v, {
                        key: v.GroupID||v.Channel,
                    })
                }));
                if(node.level == 1 && totalSize >= 30 && groupSize == 0 && this.hasAnyRole(this.serverInfo, this.userInfo, '管理员')) {
                    var tipAt = parseInt(localStorage['livenvr_screen_tip_at']) || 0;
                    var now = new Date().getTime();
                    var diff = now - tipAt;
                    if(diff >= 30 * 24 * 3600 * 1000) {
                        this.showTip = true;
                        localStorage['livenvr_screen_tip_at'] = now;
                    }
                }
                if(this.q && !node.matched) {
                    this.$refs['groupTree'].filter(this.q);
                }
            }).always(() => {
                this.$nextTick(() => {
                    this.groupTreeLoading = false;
                })
                // if(this.q && !this.q.endsWith(" ") && this.q.split(/\s+/).length === (node.level - 1)) {
                //     this.q += " ";
                //     this.$refs["q"].focus();
                // }
            })
        },
        // treeFilter(value, data, node) {
        //     if (!value) return true;
        //     var vals = value.trim().split(/\s+/);
        //     if((node.level - 1) <= 0 || (node.level - 1) > vals.length) return true;
        //     var v = vals[node.level - 2];
        //     if (!v || !data) return true;
        //     if (data.Channel && data.Channel == v) return true;
        //     return ("" + (data.Name||"") + " " + (data.GBID||data.GBID2||"")).indexOf(v) >= 0;
        // },
        treeFilter(value, data, node) {
            if (!value) return true;
            if (node.parent && node.parent.matched) {
                node.matched = true;
                return node.matched;
            }
            node.matched = ("" + (data.Name||"") + " " + (data.GBID||data.GBID2||"")).indexOf(value) >= 0;
            !node.matched && !node.isLeaf && node.expand();
            return node.matched;
        },
        treeNodeRefresh(node) {
            if(!node || !node.level) {
                this.treeRefresh();
                return
            }
            this.contextMenuVisible = false;
            node.loaded = false;
            node.expand();
        },
        treeRefresh() {
            this.showTip = false;
            this.contextMenuVisible = false;
            this.showTree = false;
            this.$nextTick(() => {
                this.showTree = true;
            })
        },
        onEditGroup(addFlag, group) {
            this.treeNodeRefresh(this.pnode);
            if (addFlag && group && this.hasAllChannel(this.serverInfo, this.userInfo)) {
                this.$confirm(`新分组 [${group.Name}] 创建成功, 现在为它分配通道吗?`, "提示", {
                    cancelButtonText: '稍候再说',
                }).then(() => {
                    this.nodeChannelListDlgTitle = `选择通道(${group.Name||'未命名'})`;
                    this.$refs['nodeChannelListDlg'].show(group.ID);
                }).catch(() => {});
            }
        },
        treeRemoveNode() {
            this.showTip = false;
            this.contextMenuVisible = false;
            if(!this.contextMenuNodeData) return;
            var action = this.contextMenuNodeData.GroupID ? "删除" : "移出";
            this.$confirm(`确认${action} ${this.contextMenuNodeData.Name}`, "提示").then(() => {
                var param = {
                    id: this.contextMenuNodeData.Channel || this.contextMenuNodeData.GroupID,
                }
                if (this.token) {
                    param.token = this.token;
                }
                $.get("/api/v1/group/remove", param).always(() => {
                    if(this.contextMenuNodeData.Channel) {
                        this.treeRefresh();
                        this.contextMenuNodeData = null;
                        this.contextMenuNode = null;
                        return
                    }
                    this.treeNodeRefresh(this.pnode);
                    this.contextMenuNodeData = null;
                    this.contextMenuNode = null;
                });
            }).catch(() => {});
        },
        treeAddNode(){
            this.showTip = false;
            this.contextMenuVisible = false;
            if(!this.contextMenuNodeData) return;
            this.$refs['nodeEditDlg'].show({
                ParentID: this.contextMenuNodeData.GroupID,
                ParentName: this.contextMenuNodeData.Name || "未命名",
            });
        },
        treeEditNode(){
            this.showTip = false;
            this.contextMenuVisible = false;
            if(!this.contextMenuNodeData) return;
            this.$refs['nodeEditDlg'].show({
                ID: this.contextMenuNodeData.GroupID,
                Name: this.contextMenuNodeData.Name,
                GBID: this.contextMenuNodeData.GBID,
                GBID2: this.contextMenuNodeData.GBID2,
                Creator: this.contextMenuNodeData.Creator,
                ParentID: this.pnode.id,
                ParentName: this.pnode.name,
            });
        },
        treeShowChannelListDlg(){
            this.showTip = false;
            this.contextMenuVisible = false;
            if(!this.contextMenuNodeData) return;
            this.nodeChannelListDlgTitle = `选择通道(${this.contextMenuNodeData.Name||'未命名'})`;
            this.$refs['nodeChannelListDlg'].show(this.contextMenuNodeData.GroupID);
        },
        treeNodeRightClick(event, data, node, c) {
            // if(!this.hasAnyRole(this.serverInfo, this.userInfo, '管理员')) return;
            this.showTip = false;
            this.contextMenuNodeData = data;
            this.contextMenuNode = node;
            this.pnode = node.parent;
            var new_event;
            if(typeof(MouseEvent) === 'function') {
                new_event = new MouseEvent(event.type, event);
            } else {
                new_event = document.createEvent('MouseEvents');
                new_event.initMouseEvent(event.type, true, true, event.view, event.detail,
                    event.screenX, event.screenY, event.clientX, event.clientY,
                    event.ctrlKey, event.altKey, event.shiftKey, event.metaKey, event.button, event);
            }
            this.contextMenuTarget.dispatchEvent(new_event);
        },
        treeNodeClick(data, node, c) {
            this.contextMenuNodeData = null;
            this.contextMenuNode = null;
            if(data.Channel && node && !node.playing) {
                if (node.timer) {
                    clearTimeout(node.timer);
                }
                if (!node.clickCnt || node.clickCnt > 1) {
                    node.clickCnt = 1;
                } else {
                    node.clickCnt++;
                }
                node.timer = setTimeout(() => {
                    node.clickCnt = 0;
                    node.timer = 0;
                }, 500);
                if(data.Online || node.clickCnt == 2) {
                    var player = this.players[this.playerIdx]||{};
                    if(player.bLoading) return;
                    this.closeVideo(this.playerIdx);
                    this.$nextTick(() => {
                        this.play(this.playerIdx, Object.assign({}, data, {Node: node}), true);
                    })
                }
            }
        },
        setPlayerIdx(idx) {
            var newIdx = idx % this.players.length;
            if(newIdx != this.playerIdx) {
                this.ctrlStop();
            }
            this.playerIdx = newIdx;
        },
        clickPlayer(player, idx, event) {
            if(!$(event.target).hasClass("video-close")) {
                this.setPlayerIdx(idx);
            }
        },
        ptzControl(e) {
            var player = this.players[this.playerIdx];
            if(!player || !player.url || (player.deviceType !== "ONVIF" && !String(player.deviceType).endsWith('SDK') && player.deviceType !== "ISUP")) return;
            var $target = $(e.currentTarget);
            var param = {
                channel: player.channel,
                speed: 5,
                command: $target.attr("command"),
            }
            if (this.token) {
                param.token = this.token;
            }
            $.get("/api/v1/ptzcontrol", param);
            $target.addClass("active");
        },
        ptzStop() {
            var player = this.players[this.playerIdx];
            if(!player || !player.url || (player.deviceType !== "ONVIF" && !String(player.deviceType).endsWith('SDK') && player.deviceType !== "ISUP")) return;
            if ($(this.$el).find(".ptz-cell.active").length > 0) {
                var param = {
                    channel: player.channel,
                    speed: 5,
                    command: "stop",
                }
                if (this.token) {
                    param.token = this.token;
                }
                $.get("/api/v1/ptzcontrol", param).always(() => {
                    if(!this.isMobile() && player.bFullscreen && player.bControls) {
                        player.closeInterval = 5000;
                        this.resetCloseTimer(player);
                    }
                })
                $(this.$el).find(".ptz-cell.active").removeClass("active");
            }
        },
        talkStart(e) {
            if(!this.ws) {
                var talkURL = this.wsTalkURL();
                if(!talkURL) return;
                this.ws = new WebSocket(talkURL);
                this.ws.onopen = evt => {
                    console.log("ws talk open");
                }
                this.ws.onclose = evt => {
                    console.log("ws talk close");
                }
                this.ws.onerror = evt => {
                    console.log("ws talk error", evt);
                }
            } else if (this.serverInfo.TalkHold) {
                this.talkStop();
                return;
            }
            var $target = $(e.currentTarget);
            if(this.recorder) {
                $target.addClass("active");
                this.recorder.start();
                return;
            }
            LiveRecorder.get((rec, err) => {
                if(err) {
                    alert(err);
                    this.talkStop();
                    return
                }
                // this.$refs["player"].setMuted(true);
                $target.addClass("active");
                this.recorder = rec;
                this.recorder.start();
            }, {
                sampleBits: 16,
                sampleRate: 8000,
                pcmCallback: (pcm, level) => {
                    // binary to base64 string
                    var reader = new window.FileReader();
                    reader.onloadend = () => {
                        var base64 = reader.result;
                        var base64 = base64.split(',')[1];
                        if(this.ws && this.ws.readyState === WebSocket.OPEN) {
                            this.ws.send(base64);
                            this.level = level;
                        }
                    }
                    reader.readAsDataURL(pcm);
                }
            })
        },
        talkStop() {
            if(this.recorder) {
                // this.recorder.stop();
                this.recorder.destroy();
                this.recorder = null;
                $(this.$el).find(".fa-microphone.active").removeClass("active");
                // this.$refs["player"].setMuted(false);
            }
            if(this.ws) {
                this.ws.close();
                this.ws = null;
                if(!this.isMobile()) {
                    var player = this.players[this.playerIdx];
                    if(player && player.bFullscreen && player.bControls) {
                        player.closeInterval = 5000;
                        this.resetCloseTimer(player);
                    }
                }
            }
            this.level = 0;
        },
        ctrlStop() {
            this.talkStop();
            this.ptzStop();
        },
        mouseUp() {
            if(!this.serverInfo.TalkHold) {
                this.talkStop();
            }
            this.ptzStop();
        },
        wsTalkURL() {
            var player = this.players[this.playerIdx];
            if(!player || !player.channel) return '';
            var protocol = "ws:";
            if(location.protocol.startsWith("https")) {
                protocol = "wss:";
            }
            return `${protocol}//${location.host}/api/v1/ws-talk?format=pcm&channel=${player.channel}`;
        },
        updateSticky() {
            $("#screen-sticky").sticky("update");
            this.$nextTick(() => {
                $("#screen-sticky").sticky("update");
            })
        },
        clearVideos() {
            if(this.loopTimer) {
                clearTimeout(this.loopTimer);
                this.loopTimer = 0;
            }
            this.loopChannelIndex = 0;
            this.loopPlayerIndex = 0;
            this.loopPlaying = false;
            this.loopChannels = [];
            this.loopInterval = 10;
            this.outHevcTipIdx = -1;
            for (var idx in this.players) {
                this.closeVideo(idx);
            }
        },
        startLoopPlay(channels, interval) {
            this.clearVideos();
            if(this.isDemoUser(this.serverInfo, this.userInfo) && this.playerLength > 4) {
                this.$message({
                    type: "error",
                    message: "演示系统请在四分屏体验, 或下载使用"
                })
                return
            }
            this.loopChannelIndex = 0;
            this.loopPlayerIndex = 0;
            this.loopPlaying = true;
            this.loopChannels = channels;
            this.loopInterval = interval;
            this.loopPlay();
        },
        stopLoopPlay() {
            this.clearVideos();
        },
        async loopPlay() {
            if (!this.loopPlaying) {
                return
            }
            if (!this.loopChannels || !this.loopChannels.length) {
                this.$message({
                    type: "error",
                    message: "没有可轮播资源"
                });
                return
            }
            let channelIdx = this.loopChannelIndex % this.loopChannels.length;
            let playerIdx = this.loopPlayerIndex % this.players.length;
            if (playerIdx == 0 && this.loopPlayerIndex > 0) { // 播放器复始
                this.loopPlayerIndex = 0;
                if (channelIdx > 0 || this.loopChannelIndex == 0) { // 点位没有复始
                    this.loopTimer = setTimeout(() => {
                        this.loopPlay();
                    }, this.loopInterval * 1000)
                    return
                }
            }
            if (channelIdx == 0 && this.loopChannelIndex > 0 && this.loopPlayerIndex == 0) { // 点位 & 播放器 复始
                this.loopChannelIndex = 0;
                this.loopTimer = setTimeout(() => {
                    this.loopPlay();
                }, this.loopInterval * 1000)
                return
            }
            if(this.bPlayerFullscreen) {
                this.loopTimer = setTimeout(() => {
                    this.loopPlay();
                }, this.loopInterval * 1000);
                return
            }
            let nextChannel = this.loopChannels[channelIdx];
            let nextPlayer = this.players[playerIdx];
            if(!nextPlayer || nextPlayer.bFullscreen) {
                this.loopPlayerIndex++; // skip this player
                this.loopPlay();
                return;
            }
            let result = await this.loopPlayChannel(nextChannel, nextPlayer);
            if (result) {
                this.loopChannelIndex++;
                this.loopPlayerIndex++;
            } else {
                // this.loopChannels.splice(this.loopChannelIndex, 1);
                this.loopChannelIndex++; // skip this channel
            }
            this.loopPlay();
        },
        async loopPlayChannel(channel, player) {
            if (!this.loopPlaying) {
                return false;
            }
            if (!channel) {
                return false;
            }

            var protocol = "HLS";
            if (this.flvSupported()) {
                if(this.players.length < 6 && !this.isIE()) {
                    protocol = "FLV";
                } else {
                    protocol = "WS_FLV";
                }
            } else if (this.rtcSupported()) {
                protocol = "WEBRTC";
            }
            switch(String(this.protocol).toUpperCase()) {
                case "WEBRTC":
                    if(this.rtcSupported()) {
                        protocol = "WEBRTC";
                    }
                    break;
                case "FLV":
                    if(this.players.length < 6 && this.flvSupported() && !this.isIE()) {
                        protocol = "FLV";
                        break;
                    }
                case "WS_FLV":
                case "WS-FLV":
                    if(this.flvSupported()) {
                        protocol = "WS_FLV";
                    }
                    break;
                case "HLS":
                    protocol = "HLS";
                    break;
            }
            var data = await this.loopGetChannelStream(channel, protocol);
            if (!data) {
                return false;
            }
            var videoUrl = String((data.LiveQing || data.NVR).Body.URL).replace("{host}", location.hostname);
            var errNum = (data.LiveQing || data.NVR).Header.ErrorNum;
            var errString = (data.LiveQing || data.NVR).Header.ErrorString;
            if (!videoUrl || errNum != "200") {
                if(errNum != "200" && errString) {
                   console.log(`get channel[${channel.Channel}] stream in loop play error: ${errNum} ${errString}`)
                }
                return false;
            }
            if (player) {
                if (protocol == "WS_FLV" && videoUrl.indexOf("ws") != 0) {
                    var prefix = "ws:";
                    if (location.protocol == "https:") {
                        prefix = "wss:";
                    }
                    videoUrl = prefix + "//" + location.host + videoUrl;
                }
                player.poster = protocol == "RTMP" ? "" : ((data.LiveQing || data.NVR).Body.SnapURL || "");
                player.title = (data.LiveQing || data.NVR).Body.ChannelName || "";
                player.osd = (data.LiveQing || data.NVR).Body.OSD;
                player.deviceType = (data.LiveQing || data.NVR).Body.DeviceType;
                player.talkEnable = (data.LiveQing || data.NVR).Body.TalkEnable || false;
                player.channel = channel.Channel;
                player.sourceVideoCodecName = channel.SourceVideoCodecName;
                player.sourceVideoWidth = channel.SourceVideoWidth;
                player.sourceVideoHeight = channel.SourceVideoHeight;
                player.protocol = protocol;
                player.url = videoUrl; // no need in next tick since player@2.6.9
            }
            return true;
        },
        loopGetChannelStream(channel, protocol) {
            return new Promise((resolve, reject) => {
                var param = {
                    Channel: channel.Channel,
                    Protocol: protocol,
                }
                if (this.token) {
                    param.token = this.token;
                }
                $.ajax({
                    method: "GET",
                    url: "/api/v1/getchannelstream",
                    global: false,
                    data:param
                }).then(data => {
                    if(!this.loopPlaying) {
                        resolve(null);
                        return
                    }
                    try {
                        if (data && (data.LiveQing || data.NVR) && (data.LiveQing || data.NVR).Body) {
                            resolve(data);
                            return
                        }
                    } catch (error) {
                        console.log("get channel stream in loop play error", error);
                    }
                    resolve(null);
                }).fail(xhr => {
                    if(xhr) {
                        console.log(`get channel stream in loop play error: ${xhr.status} ${xhr.responseText}`);
                        if(xhr.status == 401) {
                            this.$nextTick(() => {
                                location.href = `/login?r=${encodeURIComponent(window.location.href)}`;
                            })
                        }
                    }
                    resolve(null);
                })
            })
        },
        showLoopPlayListDlg(){
            this.showTip = false;
            this.$refs['loopPlayListDlg'].show();
        },
        allowDrag(draggingNode) {
            if(draggingNode.level < 2) return false;
            if(!draggingNode.data) return false;
            if(!draggingNode.parent || !draggingNode.parent.data) return false;
            if(draggingNode.data.GroupID == 'ROOT') return false;
            if(!this.hasAllChannel(this.serverInfo, this.userInfo) && this.userInfo) {
                if(draggingNode.data.GroupID && this.userInfo.id != draggingNode.data.CreatorID) {
                    return false;
                }
            }
            if(draggingNode.data.Channel || draggingNode.data.GroupID) {
                this.pnode = draggingNode.parent;
                return true;
            }
            return false;
        },
        allowDrop(draggingNode, dropNode, type) { // type=prev, inner, next
            if(draggingNode.level < 2) return false;
            if(dropNode.level < 1) return false;
            if(!draggingNode.parent || !draggingNode.data) return false;
            if(!dropNode.data || !dropNode.data.GroupID) return false;
            if(!this.hasAllChannel(this.serverInfo, this.userInfo) && this.userInfo) {
                if(this.userInfo.id != dropNode.data.CreatorID) {
                    return false;
                }
            }
            if(type == "inner") {
                return true;
            }
            return false;
        },
        handleDrop(draggingNode, dropNode, dropType, event) { // dropType=before, inner, after
            if(dropType != "inner") return;
            var param = {
                id: draggingNode.data.Channel || draggingNode.data.GroupID,
                pid: dropNode.data.GroupID,
            }
            if (this.token) {
                param.token = this.token;
            }
            $.get("/api/v1/group/move", param).always(() => {
                if(this.pnode && (!this.pnode.parent || !this.pnode.parent.level)) {
                    this.treeRefresh();
                    return
                }
                if(!dropNode.parent || !dropNode.parent.level) {
                    this.treeRefresh();
                    return
                }
                this.treeNodeRefresh(dropNode.parent);
                if(this.pnode && dropNode.parent != this.pnode.parent) {
                    this.treeNodeRefresh(this.pnode.parent);
                }
            });
        },
        playStop(node) {
            this.contextMenuVisible = false;
            if (!node || !node.play || !node.play.length) return;
            for (var idx of node.play) {
                this.closeVideo(idx, true);
            }
        },
        formatNodeTitle(node, data) {
            if (data && data.Channel) {
                return `[${data.Channel}] ${node.label}`;
            }
            return node.label;
        },
    }, //-- methods
    computed: {
        ...mapState(["userInfo", "serverInfo"]),
        playing() {
            var player = this.players[this.playerIdx] || {};
            return !!player.url;
        },
        canPTZ() {
            var player = this.players[this.playerIdx] || {};
            return player.deviceType === "ONVIF" || String(player.deviceType).endsWith('SDK') || player.deviceType === "ISUP";
        },
        talkEnable() {
            var player = this.players[this.playerIdx] || {};
            return player.talkEnable || false;
        },
        groupTreeEmptyText() {
            return this.groupTreeLoading ? "加载中..." : "暂无数据";
        }
    },
    beforeRouteEnter(to, from, next) {
        next(vm => {
            if(to.query.protocol) {
                vm.protocol = to.query.protocol;
            }
        });
    },
    beforeRouteUpdate(to, from, next) {
        this.clearVideos();
        if(to.query.protocol) {
            this.protocol = to.query.protocol;
        }
        next();
    },
    beforeDestroy() {
        this.ctrlStop();
        this.showTip = false;
        this.clearVideos();
        $(document).off("mouseup touchend", this.mouseUp);
        $(document).off('expanded.pushMenu collapsed.pushMenu', this.updateSticky);
        $("#screen-sticky").unstick();
    },
    beforeRouteLeave(to, from, next) {
        this.clearVideos();
        next()
    }
};
</script>


<style lang="less" scoped>
@import url(~assets/styles/variables.less);

.right-menu {
  position: fixed;
  background: #fff;
  border: solid 1px rgba(0, 0, 0, .2);
  border-radius: 3px;
  z-index: 999;
  display: none;
}
.right-menu a {
  width: 100px;
  height: 28px;
  line-height: 28px;
  text-align: center;
  display: block;
  color: #1a1a1a;
}
.right-menu a:hover {
  background: #eee;
  color: #fff;
}
.right-menu {
    border: 1px solid #eee;
    box-shadow: 0 0.5em 1em 0 rgba(0,0,0,.1);
    border-radius: 1px;
}
a {
    text-decoration: none;
}
.right-menu a {
    padding: 2px;
}
.right-menu a:hover {
    background: @base;
}
.view-list {
    .video-show {
        .video {
            border: 1px solid #fff;

            &.active .video-inner {
                border: 2px solid red;
            }
        }

        .col-sm-12 {
            .osd-block {
                font-size: 1.2vw;
            }

            &:nth-child(1) {
                border: 0;
            }
        }

        .col-sm-6 {
            .osd-block {
                font-size: 0.8vw;
            }

            &:nth-child(1),
            &:nth-child(2) {
                border-top-color: transparent;
            }

            &:nth-child(2),
            &:nth-child(4) {
                border-right-color: transparent;
            }

            &:nth-child(1),
            &:nth-child(3) {
                border-left-color: transparent;
            }

            &:nth-child(3),
            &:nth-child(4) {
                border-bottom-color: transparent;
            }
        }

        .col-sm-4 {
            .osd-block {
                font-size: 0.6vw;
            }

            &:nth-child(1),
            &:nth-child(2),
            &:nth-child(3) {
                border-top-color: transparent;
            }

            &:nth-child(3),
            &:nth-child(6),
            &:nth-child(9) {
                border-right-color: transparent;
            }

            &:nth-child(7),
            &:nth-child(8),
            &:nth-child(9) {
                border-bottom-color: transparent;
            }

            &:nth-child(1),
            &:nth-child(4),
            &:nth-child(7) {
                border-left-color: transparent;
            }
        }

        .col-sm-3 {
            .osd-block {
                font-size: 0.4vw;
            }

            &:nth-child(1),
            &:nth-child(2),
            &:nth-child(3),
            &:nth-child(4) {
                border-top-color: transparent;
            }

            &:nth-child(4),
            &:nth-child(8),
            &:nth-child(12),
            &:nth-child(16) {
                border-right-color: transparent;
            }

            &:nth-child(13),
            &:nth-child(14),
            &:nth-child(15),
            &:nth-child(16) {
                border-bottom-color: transparent;
            }

            &:nth-child(1),
            &:nth-child(5),
            &:nth-child(9),
            &:nth-child(13) {
                border-left-color: transparent;
            }
        }
    }
}

.fullscreen {
    width: 100% !important;
}

.video-close {
    position: absolute;
    top: 5px;
    right: 5px;
    color: white;
    font-size: 12px;
    background-color: fade(gray, 50%);
    padding: 2px 5px;
    cursor: pointer;
    border-radius: 2px;
    max-width: 120px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.video-title {
  position: absolute;
  top: 5px;
  left: 5px;
  color: white;
  font-size: 14px;
  background-color: fade(gray, 50%);
  padding: 2px 5px;
  border-radius: 2px;
  max-width: 200px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.player-btn {
    margin: 5px 0;

    .el-button--primary {
        color: #606266;
        background: #ffffff;
        border: 1px solid #dcdfe6 !important;
    }

    .active {
        background: #0c6bba;
        color: #ffffff;
    }
}

.osd-block {
    position: absolute;
    text-align: left;
    font-size: 1.75vw;
    background-color: #525a5e;
    padding: 0.4vw;
    border-radius: 2px;
    opacity: 0.8;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.video-js:fullscreen {
    .osd-block {
        font-size: 1.75vw !important;
    }
}

#group-tree {
    min-height: 200px;
    overflow: auto;
    background-color: transparent;

    @media screen and (max-width: 992px) {
        max-height:200px;
    }
}

.el-tree-node.is-drop-inner > .el-tree-node__content .custom-tree-node {
    background-color: @base;
    color: #fff;
}

#screen-sticky > #screen-sticky-bottom {
    display: none;
}
#screen-sticky-wrapper.sticky > #screen-sticky > #screen-sticky-bottom {
    display: block;
}
</style>

<!-- ptz style -->
<style lang="less" scoped>
.settings-block {
    margin: 30px auto;
    text-align: center;
}
.ptz-block {
    width: 150px;
    height: 180px;
    margin: 0 auto;
    text-align: center;
    position: relative;
    font-size: 24px;
    .ptz-cell {
        width: 50px;
        height: 50px;
        line-height: 50px;
        position: absolute;
    }
    .ptz-cell.active {
        color: #ccc;
        font-size: 26px;
    }
    .ptz-cell.readonly {
        color: #ddd;
    }
    .fa-microphone.active {
        color: #FFF;
    }
    .fa-microphone.readonly {
        color: #ddd;
    }
    .fa-microphone-slash.readonly {
        color: #ddd;
    }
    .mic-level-bar {
        position: absolute;
        transform: rotate(-90deg);
        width: 100%;
        top: 70px;
        left: -80px;
    }
    .ptz-center {
        top: 50px;
        left: 50px;
        border-radius: 25px;
        background-color: #ccc;
    }
    .ptz-up {
        top: 0;
        left: 50px;
    }
    .ptz-left {
        top: 50px;
        left: 0;
    }
    .ptz-right {
        top: 50px;
        left: 100px;
    }
    .ptz-down {
        top: 100px;
        left: 50px;
    }
    .ptz-zoomin {
        top: 150px;
        left: 20px;
    }
    .ptz-zoomout {
        top: 150px;
        left: 80px;
    }
    .ptz-talk {
        top: 150px;
        left: 50px;
    }
    .ptz-up,
    .ptz-left,
    .ptz-right,
    .ptz-down,
    .ptz-center > .fa-microphone,
    .ptz-zoomin,
    .ptz-talk > .fa-microphone,
    .ptz-zoomout {
        cursor: pointer;
        &.readonly {
            cursor: auto;
        }
    }
}

.ptz-block-fs {
  position: fixed;
  margin: 0;
  margin-bottom: 50px;
  top: 0;
  right: 0;
  height: 200px;
  // border-radius: 16px;
  // background: fade(#eee, 20%);
  .ptz-center {
    cursor: move;
    position: absolute;
  }
  .fa-microphone.active {
    color: #ccc;
  }
  .ptz-zoomin {
    left: 15px;
  }
  .ptz-zoomout {
    left: 85px;
  }
}
</style>

<style lang="less">
.ptz-block {
  .mic-level-bar {
    .el-progress-bar__outer {
      background-color: #ddd !important;
    }
  }
}

.view-list {
    .video-show {
        :not(:fullscreen) {
            .video.active .video-inner {
                border: 2px solid red;
            }
        }
    }
}

.fullscreen {
    & > .video {
        &.col-sm-12 {
            height: 100%;
        }
        &.col-sm-6 {
            height: 50%;
        }
        &.col-sm-4 {
            height: 33.33%;
        }
        &.col-sm-3 {
            height: 25%;
        }
        & > .player-wrapper {
            height: 100%;
            .video-wrapper {
                padding-bottom: 0 !important;
                height: 100%;
            }
        }
    }
}
</style>
